﻿using LightCAD.Core.Element3d;
using LightCAD.MathLib;
using LightCAD.Three;
using System;
using System.Collections.Generic;
using static System.Windows.Forms.DataFormats;

namespace QdArch
{
    public class RampAttribute : CptAttribute
    {
        public RampAttribute(string category, string subCategorys) : base(category, subCategorys)
        {
        }
        public const string BuiltinUuid = "{6c48f24c-fe61-4820-a752-2463d60187a7}";

        public override LcComponentDefinition GetBuiltinCpt(string subCategory)
        {
            return new QdRampRef(BuiltinUuid,"内置", "坡道");
        }
    }
    [RampAttribute("坡道", "坡道")]
    public class QdRampRef : LcComponentDefinition
    {
        public QdRampRef()
        {
            this.TypeParameters = new LcParameterSet(CptTypeParamsDef<QdRampRef>.ParamsDef);
        }
        static QdRampRef()
        {
            CptTypeParamsDef<QdRampRef>.ParamsDef = new ParameterSetDefinition { new ParameterDefinition { DataType = LcDataType.String, Name = "MaterialId", DisplayName = "材质", } };
        }
        public QdRampRef(string uuid, string name, string subCategory) : base(uuid, name, "坡道", subCategory, new LcParameterSet(CptTypeParamsDef<QdRampRef>.ParamsDef))
        {
            this.Parameters = new ParameterSetDefinition()
            {  
                new ParameterDefinition
                {
                Name = "RampWidth",
                DataType= LcDataType.Double,
                },
                new ParameterDefinition
                {
                Name = "SlabThickness",
                DataType= LcDataType.Double,
                },
                new ParameterDefinition
                {
                Name = "ElevationStart",
                DataType= LcDataType.Double,
                },
                new ParameterDefinition
                {
                Name = "ElevationEnd",
                DataType= LcDataType.Double,
                },
                new ParameterDefinition
                {
                Name = "BaseLine",
                DataType= LcDataType.Curve2d,
                },
                new ParameterDefinition
                {
                Name = "Superelevation",
                DataType= LcDataType.Double,
                },
                  new ParameterDefinition
                {
                Name = "GentleSlope",
                DataType= LcDataType.Double,
                },
            };
            this.Curve2dProvider = new Curve2dProvider("坡道")
            {
                GetCurve2dsFunc = (pset) =>
                {
                    var curves = new List<Curve2d>();
                    var slabThickness = pset.GetValue<double>("SlabThickness");
                    var rampWidth = pset.GetValue<double>("RampWidth");
                    var elevationStart = pset.GetValue<double>("ElevationStart");
                    var elevationEnd = pset.GetValue<double>("ElevationEnd");
                    var baseLine = pset.GetValue<Curve2d>("BaseLine");
                    if (baseLine is  Line2d line)
                    {
                        var lLine= line.Clone().Translate(line.Dir.Y * rampWidth / 2, -line.Dir.X * rampWidth / 2) as Line2d;
                        var rLine = line.Clone().Translate(-line.Dir.Y * rampWidth / 2, line.Dir.X * rampWidth / 2).Reverse() as Line2d;
                        curves.Add(lLine);
                        curves.Add(new Line2d(lLine.End.Clone(),rLine.Start.Clone()));
                        curves.Add(rLine);
                        curves.Add(new Line2d(rLine.End.Clone(), lLine.Start.Clone()));
                    }
                    else if (baseLine is Arc2d arc)
                    {
                        var lArc= arc.Clone() as Arc2d;
                        lArc.Radius = arc.Radius - rampWidth / 2;
                        var lps = lArc.GetPoints(1);
                        var rArc = arc.Clone() as Arc2d;
                        rArc.Radius = arc.Radius + rampWidth / 2;
                        var rps = rArc.GetPoints(1);
                        curves.Add(lArc);
                        curves.Add(new Line2d(lps.Last(),rps.Last()));
                        curves.Add(rArc);
                        curves.Add(new Line2d(rps.First(), lps.First()));
                    }
                    curves.Add(baseLine.Clone());
                    return new Curve2dGroupCollection() { new Curve2dGroup()
                        {
                            Curve2ds=curves.ToListEx()
                        } };

                }
            };
            var mat = MaterialManager.GetMaterial(MaterialManager.ConcreteUuid);
            this.Solid3dProvider = new Solid3dProvider("坡道")
            {
                AssignMaterialsFunc = (c, s) => {
                    return new LcMaterial[] { mat };
                }
            };
        }
    }
    public class QdRampInstance : LcComponentInstance
    {
        public double RampWidth { get => Parameters.GetValue<double>("RampWidth"); set => Set("RampWidth", value); }
        public double SlabThickness { get => Parameters.GetValue<double>("SlabThickness"); set => Set("SlabThickness", value); }
        public double ElevationStart{ get => Parameters.GetValue<double>("ElevationStart"); set => Set("ElevationStart", value); }
        public double ElevationEnd { get => Parameters.GetValue<double>("ElevationEnd"); set => Set("ElevationEnd", value); }
        public double Superelevation { get => Parameters.GetValue<double>("Superelevation"); set => Set("Superelevation", value); }
        public double GentleSlope { get => Parameters.GetValue<double>("GentleSlope"); set => Set("GentleSlope", value); }

        public Curve2d BaseLine { get => Parameters.GetValue<Curve2d>("BaseLine"); set => Set("BaseLine", value); }
        public QdRampInstance(QdRampRef Ramp) : base(Ramp)
        {
            this.Type = ArchElementType.Ramp;
        }
 
        public QdRampInstance(double rampWidth, double elevationStart, double elevationEnd, double SlabThickness,Curve2d baseLine, QdRampRef Ramp) : this(Ramp)
        {
            this.Type = ArchElementType.Ramp; 
            this.RampWidth = rampWidth;
            this.ElevationStart = elevationStart;
            this.ElevationEnd = elevationEnd;
            this.SlabThickness = SlabThickness;
            this.BaseLine = baseLine;
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!(thisBox.IntersectsBox(testPoly.BoundingBox)||thisBox.ContainsBox(testPoly.BoundingBox)))
            {
                return false;
            }
            var curves = this.Curves.FirstOrDefault()?.Curve2ds;
            if (this.BaseLine!=null)
            {
                foreach (var curve in curves)
                {
                    switch (curve.Type)
                    {
                        case Curve2dType.Line2d:
                            var line = curve as Line2d;
                            if (GeoUtils.IsPolygonIntersectLine(testPoly.Points, line.Start,line.End) || GeoUtils.IsPolygonContainsLine(testPoly.Points, line.Start, line.End)) {
                                return true;
                            }
                            break;
                        case Curve2dType.Arc2d:
                            var arc = curve as Arc2d;
                            if (GeoUtils.IsPolygonIntersectArc(testPoly.Points, arc) || GeoUtils.IsPolygonContainsPolygon(testPoly.Points, arc.GetPoints()))
                            {
                                return true;
                            }
                            break;
                    }
                }
            }
            return false;
        }
        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            if (!testPoly.BoundingBox.ContainsBox(thisBox))
            {
                return false;
            }
            return true;
        }
        public void Set(string name, object val)
        {
            OnPropertyChangedBefore(name, this.Parameters[name], val);
            this.Parameters.SetValue(name, val);
            OnPropertyChangedAfter(name, this.Parameters[name], val);
        }
        public override LcElement Clone()
        {
            var clone = new QdRampInstance(this.Definition as QdRampRef);
            clone.Copy(this);
            clone.Initilize(this.Document);
            return clone;
        }
        public override Box2 GetBoundingBox()
        {
            if (this.Parameters == null || this.Parameters.Values.Length == 0)
            {
                return Box2.Empty;
            }
            var points = new List<Vector2>();
            if (this.BaseLine is Line2d line)
            {
                var lLine = line.Clone().Translate(line.Dir.Y * this.RampWidth / 2, -line.Dir.X * this.RampWidth / 2) as Line2d;
                var rLine = line.Clone().Translate(-line.Dir.Y * this.RampWidth / 2, line.Dir.X * this.RampWidth / 2) as Line2d;
                points.AddRange(lLine.GetPoints(1));
                points.AddRange(lLine.GetPoints(1));
            }
            else if (this.BaseLine is Arc2d arc)
            {
                var lArc = arc.Clone() as Arc2d;
                lArc.Radius = arc.Radius - this.RampWidth / 2;
                var rArc = arc.Clone() as Arc2d;
                rArc.Radius = arc.Radius + this.RampWidth / 2;
                points.AddRange(lArc.GetPoints(10));
                points.AddRange(rArc.GetPoints(10));
            }
            var box2d = new Box2().MakeEmpty();
            box2d = box2d.ExpandByPoints(points.Select(n => n.Add(this.Position.ToVector2())).ToArray());
            return box2d;

        }
        public override Box2 GetBoundingBox(Matrix3 matrix)
        {
            if (this.Parameters == null || this.Parameters.Values.Length == 0)
            {
                return Box2.Empty;
            }
            var points = new List<Vector2>();
            if (this.BaseLine is Line2d line)
            {
                var lLine = line.Clone().Translate(line.Dir.Y * this.RampWidth / 2, -line.Dir.X * this.RampWidth / 2) as Line2d;
                var rLine = line.Clone().Translate(-line.Dir.Y * this.RampWidth / 2, line.Dir.X * this.RampWidth / 2) as Line2d;
                points.AddRange(lLine.GetPoints(1));
                points.AddRange(lLine.GetPoints(1));
            }
            else if (this.BaseLine is Arc2d arc)
            {
                var lArc = arc.Clone() as Arc2d;
                lArc.Radius = arc.Radius - this.RampWidth / 2;
                var rArc = arc.Clone() as Arc2d;
                rArc.Radius = arc.Radius + this.RampWidth / 2;
                points.AddRange(lArc.GetPoints(10));
                points.AddRange(rArc.GetPoints(10));
            }
            var box2d = new Box2().MakeEmpty();
            box2d = box2d.ExpandByPoints(points.Select(n => matrix.MultiplyPoint(n.Add(this.Position.ToVector2()))).ToArray());
            return box2d;

        }
        public override void Copy(LcElement src)
        {
            base.Copy(src);
            var RampRef = (QdRampInstance)src;
            this.Parameters = RampRef.Parameters.Definition.New();
            this.Definition = RampRef.Definition;
            var count = RampRef.Parameters.Values.Count();
            for (int i = 0; i < count; i++)
            {
                this.Parameters[i] = RampRef.Parameters[i];
            }

        }

        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);
        }

        public override void ReadProperties(ref JsonElement jele)
        {
            base.ReadProperties(ref jele);
        }
    }
}
